package com.zjpavt.client.udp;

import com.zjpavt.client.Connection;
import com.zjpavt.client.event.EventRegisterCenter;
import com.zjpavt.client.hal.Buzzer;
import com.zjpavt.client.hal.DeviceID;
import com.zjpavt.client.hal.KeyPad;
import com.zjpavt.client.hal.LoopManager;
import com.zjpavt.client.hal.network.NetworkManager;
import com.zjpavt.client.util.ClientConfig;
import com.zjpavt.client.util.LoggerUtil;
import com.zjpavt.client.util.StringUtil;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InterfaceAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 用于接收按键的UDP监听
 * @author zyc
 */
public class KeyPadUdpServer {


    private static final Logger log = LoggerFactory.getLogger(UdpServer.class);
    private static final ThreadFactory threadFactory = new DefaultThreadFactory("keyPad");
    private static class InnerClass {
        static final KeyPadUdpServer keyPadUdpServer = new KeyPadUdpServer();
    }

    public static KeyPadUdpServer getInstance() {
        return KeyPadUdpServer.InnerClass.keyPadUdpServer;
    }

    private KeyPadUdpServer() {
        Thread thread = threadFactory.newThread(()-> {
            initServer(ClientConfig.OUTPUT_UDP_SERVER_PORT);
        });
        thread.start();
    }
    private int port;
    private void initServer(int port) {
        final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1,new DefaultThreadFactory("KeyPadUdpServer"));
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.channel(NioDatagramChannel.class);
        bootstrap.group(nioEventLoopGroup);
        bootstrap.handler(new ChannelInitializer<NioDatagramChannel>() {
            @Override
            protected void initChannel(NioDatagramChannel ch) throws Exception {
                ch.pipeline().addLast(new KeyPadUdpServer.UdpServerHandler());
            }
        });
        try {
//            发现会异常结束 对该方法进行自旋
            for (; ; ) {
                ChannelFuture bind = bootstrap.bind(port).sync();
//			蜂鸣器开启100ms
                Buzzer.buzzer(1, 100, 0, true);
                this.port = port;
                log.info("start UdpServer success!");
                bind.channel().closeFuture().await();
                log.error("UdpServer stop------------------------------------------------------");
                Thread.sleep(5000);
            }
        } catch (Exception e) {
            log.error(LoggerUtil.convertException2String(e));
        }
        finally {
            nioEventLoopGroup.shutdownGracefully();
        }
    }

    private class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            log.error(LoggerUtil.convertException2String(cause));
            log.error(cause.getMessage());
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) {
            try {
                ByteBuf buf = msg.copy().content();
                byte[] req = new byte[buf.readableBytes()];
                buf.readBytes(req);
                ReferenceCountUtil.release(buf);
                String res = new String(req, ClientConfig.DEFAULT_ENCODING);
                log.trace("received from " + msg.sender().getHostString() + ":" + res);
                //过滤掉非本机发送的udp信息，增加安全性
                String host = msg.sender().getHostString();
                if (ClientConfig.OUTPUT_UDP_SERVER_IP.equals(host)) {
                    dispatchMsg(res);
                } else {
                    Map<String, Object> data = new HashMap<>();
                    data.put(EventRegisterCenter.PORT, port);
                    data.put(EventRegisterCenter.MSG, res);
                    data.put(EventRegisterCenter.SRC_IP, host);
                    EventRegisterCenter.getInstance().triggerRegisterTask(EventRegisterCenter.UDP_RECEIVED, data);
                    String temp = res.trim();
                    if ("ask".equals(res.trim())) {
                        InterfaceAddress interfaceAddress = NetworkManager.getInstance().getInterfaceAddress(NetworkManager.INTERFACE_ETH_0);
                        int[] status = LoopManager.getInstance().getEighthLoopStatus();
                        StringBuilder returnMsg = new StringBuilder();
                        returnMsg.append("ip=")
                        .append(interfaceAddress != null ? interfaceAddress.getAddress().getHostAddress() : "unknown")
                        .append(" devcieId=")
                        .append(DeviceID.getINSTANCE().getDeviceIdCached())
                        .append(" version=")
                        .append(ClientConfig.VERSION)
                        .append(" loopStatus=")
                        .append(StringUtil.commandInt2String(status));
                        ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(returnMsg.toString().getBytes()), msg.sender()));
                    } else if ("doorOpen".equals(res.trim())) {
                        log.info("doorOpen send");
                        Buzzer.buzzer(1,500,500,true);
                        Connection.getInstance().appendMsgToQueue("<O_door_alarm>");
                    }
                }
            } catch (Exception e) {
                log.error(LoggerUtil.convertException2String(e));
            }
        }
    }

    private final static Pattern KEYPAD_PATTERN = Pattern.compile("^KeyPressed value:(0x[0-9,a-f]{1,2})$");
    private final static Pattern DOOR_PATTERN = Pattern.compile("^<DOOR_([a-z])>\\n$");
    private void dispatchMsg(String msg) {
        log.info("udp server receive a message at port 6464 :" + msg);
        Matcher matcher = KEYPAD_PATTERN.matcher(msg.trim());
        if (matcher.matches()) {
            KeyPad.getInstance().notifyChanged(matcher.group(1));
        }
    }

    public interface UdpDispatch {
        public void notifyChanged(String udpMsg);
    }
}
